require('./init')
var http = require('http');
var express = require("express");
var RED = require("bihua-red");
try {
    bcrypt = require('bcrypt');
} catch(e) {
    bcrypt = require('bcryptjs');
}

const { redSettings, commonSettings } = require('./user_settings')
// Create an Express app
var app = express();

// Add a simple route for static content served from 'public'
app.use("/static",express.static("public"));

// Create a server
var server = http.createServer(app);

// Initialise the runtime with a server and settings
RED.init(server,redSettings);

// Serve the editor UI from /red
if (redSettings.httpAdminRoot !== false) {
    app.use(redSettings.httpAdminRoot,RED.httpAdmin);
}

// Serve the http nodes UI from /api
if (redSettings.httpNodeRoot !== false && redSettings.httpNodeAuth) {
    app.use(redSettings.httpNodeRoot,basicAuthMiddleware(redSettings.httpNodeAuth.user,redSettings.httpNodeAuth.pass));
}
if (redSettings.httpNodeRoot !== false && redSettings.nodeAuth) {
    app.use(redSettings.httpNodeRoot,tokensAuthMiddleware(redSettings.nodeAuth.tokens));
}
if(redSettings.httpNodeRoot !== false){
    app.use(redSettings.httpNodeRoot,RED.httpNode);
}
if (commonSettings.port === undefined){
    commonSettings.port = 1880;
}
server.listen(commonSettings.port, function () {
    console.log(`--------server is running on port:${commonSettings.port}------------`)
});

// Start the runtime
RED.start();

function basicAuthMiddleware(user,pass) {
    var basicAuth = require('basic-auth');
    var checkPassword;
    var localCachedPassword;
    if (pass.length == "32") {
        // Assume its a legacy md5 password
        checkPassword = function(p) {
            return crypto.createHash('md5').update(p,'utf8').digest('hex') === pass;
        }
    } else {
        checkPassword = function(p) {
            return bcrypt.compareSync(p,pass);
        }
    }

    var checkPasswordAndCache = function(p) {
        // For BasicAuth routes we know the password cannot change without
        // a restart of Node-RED. This means we can cache the provided crypted
        // version to save recalculating each time.
        if (localCachedPassword === p) {
            return true;
        }
        var result = checkPassword(p);
        if (result) {
            localCachedPassword = p;
        }
        return result;
    }

    return function(req,res,next) {
        if (req.method === 'OPTIONS') {
            return next();
        }
        var requestUser = basicAuth(req);
        if (!requestUser || requestUser.name !== user || !checkPasswordAndCache(requestUser.pass)) {
            res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
            return res.sendStatus(401);
        }
        next();
    }
};

function tokensAuthMiddleware(tokens) {
    return function(req,res,next) {
        if (req.method === 'OPTIONS') {
            return next();
        }
        var token = null;
        if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
            token = req.headers.authorization.split(' ')[1];
        }
        if(token == null){
            res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
            return res.sendStatus(401);
        }
        tokens(token).then(function(user) {
            if (user) {
                next();
            } else {
                res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
                return res.sendStatus(401);
            }
        }).catch(function(e) {
            res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
            return res.sendStatus(401);
        });
    }
};